#ifndef WALLE_ALGO_LEXIAL_CAST_H_
#define WALLE_ALGO_LEXIAL_CAST_H_
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <stdexcept>
#include <typeinfo>
#include <cstring>
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <type_traits>

namespace walle {

class walle_error : public std::exception {
public:
    walle_error(const std::string &msg): _msg(msg){}
    ~walle_error() throw() {}
    const char *what() const throw() { return _msg.c_str(); }
private:
    std::string _msg;
};

template <typename Target, typename Source, bool Same>
class lexical_cast_t{
public:
static bool cast(const Source &arg, Target &ret)
{
  	std::stringstream ss;
  	try {
    		ss<<arg;
    		ss>>ret;
    		if(!ss.eof()) {
    			return false;
    		}
  	} catch(const std::exception & e) {
  		  return false;
  	}
  	return true;
}
};

template <typename Target, typename Source>
class lexical_cast_t<Target, Source, true>{
public:
static bool cast(const Source &arg, Target &ret)
{
  	ret = arg;
  	return true; 
}  
};

template <typename Source>
class lexical_cast_t<std::string, Source, false>{
public:
	static bool cast(const Source &arg, std::string &ret)
	{
  		std::ostringstream ss;
  		try {
    			ss<<arg;
    			ret = std::move(ss.str());
  		} catch(const std::exception &e) {
  			 return false;
  		}
  		return true;
	}
};

template <typename Target>
class lexical_cast_t<Target, std::string, false>{
public:
	static bool cast(const std::string &arg, Target &ret)
	{
	  	try{
  	    	std::istringstream ss(arg);
  	    	ss>>ret;
  	    	if(!ss.eof()) {
  	    		return false;
  	    	}
		} catch(const std::exception &e) {
			 return false;
		}

	     return true;
	}
};

template<typename Target, typename Source>
bool lexical_cast(const Source &arg, Target &ret)
{
  	return lexical_cast_t<Target, Source, std::is_same<Target, Source>::value>::cast(arg, ret);
}

template <class T>
std::string default_value(T def)
{
    std::string ret;
    if(!lexical_cast<std::string>(def, ret)) {
        return "";
    }

  	return ret;
}

template <class T>
struct default_reader{
	bool operator()(const std::string &str, T &ret)
	{
		  return lexical_cast<T>(str, ret);
	}
};

template <class T>
struct range_reader{
range_reader(const T &low, const T &high): _low(low), _high(high) 
{
	assert(_low <= _high);
}
bool operator()(const std::string &s, T &ret) const 
{
	if(!default_reader<T>()(s, ret)) {
		  return false;
	}
	if (!(ret>=_low && ret<=_high)) {
		  return false;
	}
	return true;
}

bool operator()(const T &s)
{
  	if (!(s>=_low && s<=_high)) {
  		  return false;
  	}
  	return true;	
}
private:
    T _low, _high;
};

template <class T>
range_reader<T> range(const T &low, const T &high)
{
    return range_reader<T>(low, high);
}

template <class T>
struct oneof_reader{
	bool operator()(const std::string &s, T &ret)
	{
  		if(!default_reader<T>()(s, ret)) {
  			 return false;
  		}
  		if (std::find(alt.begin(), alt.end(), ret)==alt.end()) {
  			 return false;
  		}
  		return true;
	}

	bool operator()(const T &s) 
	{
		if (std::find(alt.begin(), alt.end(), s)==alt.end()) {
			 return false;
		}
		return true;	
	}
  	void add(const T &v){ alt.push_back(v); }
private:
    std::vector<T> alt;
};


template <class T>
oneof_reader<T> oneof(T a1)
{
    oneof_reader<T> ret;
    ret.add(a1);
    return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2)
{
    oneof_reader<T> ret;
    ret.add(a1);
    ret.add(a2);
    return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3)
{
    oneof_reader<T> ret;
    ret.add(a1);
    ret.add(a2);
    ret.add(a3);
    return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4)
{
    oneof_reader<T> ret;
    ret.add(a1);
    ret.add(a2);
    ret.add(a3);
    ret.add(a4);
    return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5)
{
    oneof_reader<T> ret;
    ret.add(a1);
    ret.add(a2);
    ret.add(a3);
    ret.add(a4);
    ret.add(a5);
    return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6)
{
    oneof_reader<T> ret;
    ret.add(a1);
    ret.add(a2);
    ret.add(a3);
    ret.add(a4);
    ret.add(a5);
    ret.add(a6);
    return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7)
{
    oneof_reader<T> ret;
    ret.add(a1);
    ret.add(a2);
    ret.add(a3);
    ret.add(a4);
    ret.add(a5);
    ret.add(a6);
    ret.add(a7);
    return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8)
{
    oneof_reader<T> ret;
    ret.add(a1);
    ret.add(a2);
    ret.add(a3);
    ret.add(a4);
    ret.add(a5);
    ret.add(a6);
    ret.add(a7);
    ret.add(a8);
    return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9)
{
    oneof_reader<T> ret;
    ret.add(a1);
    ret.add(a2);
    ret.add(a3);
    ret.add(a4);
    ret.add(a5);
    ret.add(a6);
    ret.add(a7);
    ret.add(a8);
    ret.add(a9);
    return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, T a10)
{
    oneof_reader<T> ret;
    ret.add(a1);
    ret.add(a2);
    ret.add(a3);
    ret.add(a4);
    ret.add(a5);
    ret.add(a6);
    ret.add(a7);
    ret.add(a8);
    ret.add(a9);
    ret.add(a10);
    return ret;
}

}

#endif